﻿using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Cache;

namespace Wicture.QPZS.EPC.API.Domain
{
    /// <summary>
    /// 访问控制
    /// </summary>
    public class ThrottlingController
    {
        private DefaultRepository repository;
        private ICacheProvider cacheProvider;

        private const int ExpirationInSeconds = 60;

        public void Init()
        {
            ApiInvocationHandler.Invoking += ApiInvocationHandler_Invoking;
            ApiInvocationHandler.Invoked += ApiInvocationHandler_Invoked;

            cacheProvider = CacheProviderFactory.CacheProviders.Values.FirstOrDefault();
            repository = new DefaultRepository();
        }

        private void ApiInvocationHandler_Invoking(object sender, ApiInvokingArgs e)
        {
            var servicePackage = repository.GetServicePackageForAccount(e.Context.Identity.Id);
            if (servicePackage == null)
            {
                throw new LogicalException("当前账号没有有效服务。", 601);
            }

            if (servicePackage.Rest <= 0)
            {
                throw new LogicalException("账号余量不足。", 602);
            }
            var key = GetFrequencyRestrictionKey(e.Context.Identity.Id);
            InvocationFrequency invocationFrequency = cacheProvider.Get(key)?.ToObject<InvocationFrequency>();
            bool expired = false;
            if (invocationFrequency == null)
            {
                invocationFrequency = new InvocationFrequency { ApiCounters = new List<ApiCounter>(), Stamp = DateTime.UtcNow };
            }
            else
            {
                expired = DateTime.UtcNow - invocationFrequency.Stamp > TimeSpan.FromSeconds(ExpirationInSeconds);
                if (expired)
                {
                    invocationFrequency = new InvocationFrequency { ApiCounters = new List<ApiCounter>(), Stamp = DateTime.UtcNow };
                }
            }
            var counter = invocationFrequency.ApiCounters.FirstOrDefault(a => a.Name == e.Context.Api.name);
            if (counter == null)
            {
                counter = new ApiCounter { Name = e.Context.Api.name };
                invocationFrequency.ApiCounters.Add(counter);
            }

            bool needDefinition = true;
            var servicePackageApis = repository.GetServicePackageApis(e.Context.Identity.Id);
            if (servicePackageApis.Any())
            {
                var api = servicePackageApis.FirstOrDefault(q => q.Name.Equals(e.Context.Api.name, StringComparison.OrdinalIgnoreCase));
                if (api == null)
                {
                    throw new LogicalException("当前账号没有开通该接口访问权限。", 601);
                }
                if (api.Total <= api.Consumed)
                {
                    throw new LogicalException($"{api.Name}接口余量不足。", 602);
                }
                if (api.Frequency > 0)
                {
                    if (counter?.Count > api.Frequency)
                    {
                        LoggerManager.Logger.LogWarning($"{e.Context.Identity.Name}: 调用频率超出: {invocationFrequency.Total} / {api.Frequency}.");
                        throw new LogicalException($"{api.Name}接口调用太频繁，超出每分钟{api.Frequency}次。", 603);
                    }
                    needDefinition = false;
                }

            }

            if (servicePackage.Frequency > 0)
            {

                if (invocationFrequency?.Total > servicePackage.Frequency)
                {
                    LoggerManager.Logger.LogWarning($"{e.Context.Identity.Name}: 调用频率超出: {invocationFrequency.Total} / {servicePackage.Frequency}.");
                    throw new LogicalException($"接口调用太频繁，超出每分钟{servicePackage.Frequency}次。", 603);
                }

                if (needDefinition)
                {
                    var definition = repository.ApiDefinitions.Value[e.Context.Api.name];
                    if (counter?.Count > definition.Frequency)
                    {
                        LoggerManager.Logger.LogWarning($"{e.Context.Identity.Name}: {definition.Name}调用频率超出: {counter.Count} / {definition.Frequency}.");
                        throw new LogicalException($"接口调用太频繁：超出每分钟{definition.Frequency}次。", 604);
                    }
                }
            }

            // 更新缓存
            invocationFrequency.Total += 1;
            counter.Count += 1;
            cacheProvider.Set(key, invocationFrequency, ExpirationInSeconds);

            e.Context.HttpContext.Items.Add("PackageType", servicePackage.PackageType);
        }

        private void ApiInvocationHandler_Invoked(object sender, ApiInvokedArgs e)
        {
            // 记录日志
            var packageType = (int)e.Context.HttpContext.Items["PackageType"];
            decimal cost = 1;
            if (packageType != 1)
            {
                var definition = repository.ApiDefinitions.Value[e.Context.Api.name];

                cost = e.Context.HttpContext.Request.Query.Any(q => q.Key.Equals("vin", StringComparison.InvariantCultureIgnoreCase))
                    ? (definition.PriceWithVin?? definition.PriceWithoutVin)
                    : definition.PriceWithoutVin;
            }

            repository.LogInvocation(e.Context.Api.name, e.Context.Identity.Id, cost, e.Context.HttpContext.Request.QueryString.Value);
        }

        private string GetFrequencyRestrictionKey(string id)
        {
            return $"API_{id}";
        }
    }
}
